import asyncio
import base64
import hashlib
import hmac
import json
import time
import uuid
import websockets
from typing import Optional, Tuple, List, AsyncGenerator
import os
from urllib.parse import urlencode
from datetime import datetime
from time import mktime
from wsgiref.handlers import format_date_time

from core.providers.tts.base import TTSProviderBase
from config.logger import setup_logging

TAG = __name__
logger = setup_logging()


class TTSProvider(TTSProviderBase):
    """
    讯飞在线语音合成（流式版）Provider
    文档: https://www.xfyun.cn/doc/tts/online_tts/API.html
    """
    HOST = "tts-api.xfyun.cn"
    PATH = "/v2/tts"
    REQUEST_URL = f"wss://{HOST}{PATH}"

    def __init__(self, config: dict, delete_audio_file: bool = True):
        super().__init__(config, delete_audio_file)
        self.app_id = config.get("appid")
        self.api_key = config.get("api_key")
        self.api_secret = config.get("api_secret")
        # aue: lame for mp3, raw for pcm
        self.aue = config.get("aue", "lame")
        self.auf = config.get("auf", "audio/L16;rate=16000")
        self.vcn = config.get("vcn", "xiaoyan")
        self.speed = int(config.get("speed", 50))
        self.volume = int(config.get("volume", 50))
        self.pitch = int(config.get("pitch", 50))

    def _get_auth_url(self) -> str:
        """
        生成符合讯飞语音合成V2鉴权方式的URL
        """
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))

        signature_origin = f"host: {self.HOST}\n"
        signature_origin += f"date: {date}\n"
        signature_origin += f"GET {self.PATH} HTTP/1.1"

        signature_sha = hmac.new(self.api_secret.encode('utf-8'), signature_origin.encode('utf-8'),
                                 digestmod=hashlib.sha256).digest()
        signature = base64.b64encode(signature_sha).decode('utf-8')

        authorization_origin = f'api_key="{self.api_key}", algorithm="hmac-sha256", headers="host date request-line", signature="{signature}"'
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode('utf-8')

        params = {
            "authorization": authorization,
            "date": date,
            "host": self.HOST
        }
        return self.REQUEST_URL + "?" + urlencode(params)

    async def _stream_generate(self, text: str, voice: Optional[str] = None) -> AsyncGenerator[bytes, None]:
        """
        私有方法：流式生成音频数据
        """
        if not all([self.app_id, self.api_key, self.api_secret]):
            logger.bind(tag=TAG).error("讯飞语音合成配置不完整")
            return

        auth_url = self._get_auth_url()
        vcn = voice if voice else self.vcn

        try:
            async with websockets.connect(auth_url) as websocket:
                request_payload = {
                    "common": {"app_id": self.app_id},
                    "business": {
                        "aue": self.aue, "auf": self.auf, "vcn": vcn,
                        "speed": self.speed, "volume": self.volume,
                        "pitch": self.pitch, "tte": "UTF8"
                    },
                    "data": {
                        "text": base64.b64encode(text.encode('utf-8')).decode('utf-8'),
                        "status": 2
                    }
                }
                await websocket.send(json.dumps(request_payload))

                while True:
                    try:
                        message = await websocket.recv()
                        message_json = json.loads(message)
                        if message_json.get("code") != 0:
                            logger.bind(tag=TAG).error(f"讯飞TTS返回错误: {message}")
                            break
                        
                        audio_base64 = message_json.get("data", {}).get("audio")
                        if audio_base64:
                            yield base64.b64decode(audio_base64)

                        if message_json.get("data", {}).get("status") == 2:
                            break
                    except websockets.exceptions.ConnectionClosed:
                        break
        except Exception as e:
            logger.bind(tag=TAG).error(f"讯飞TTS处理失败: {e}", exc_info=True)
            
    def generate_filename(self, extension: Optional[str] = None) -> str:
        """
        生成并返回音频文件的完整路径，扩展名由aue决定
        """
        file_extension = "mp3" if "lame" in self.aue else "pcm"
        if extension:
            file_extension = extension.lstrip('.')
            
        file_name = f"tts_xunfei_{datetime.now().strftime('%Y%m%d%H%M%S')}_{uuid.uuid4().hex[:8]}.{file_extension}"
        return os.path.join(self.output_file, file_name)

    async def text_to_speak(self, text: str, output_file: str):
        """
        符合基类定义的文本转语音方法
        """
        # 确保目录存在
        os.makedirs(os.path.dirname(output_file), exist_ok=True)
        # 先创建一个空文件
        with open(output_file, "wb") as f:
            pass

        try:
            # 使用追加模式写入音频流
            with open(output_file, "ab") as f:
                async for audio_chunk in self._stream_generate(text):
                    f.write(audio_chunk)
        except Exception as e:
            logger.bind(tag=TAG).error(f"讯飞TTS音频流写入文件失败: {output_file}, {e}", exc_info=True)
            # 如果出错，尝试删除可能已创建的不完整文件
            if os.path.exists(output_file):
                os.remove(output_file)
            raise e # 重新抛出异常，以便上层调用者知道发生了错误
